import json
import threading
import time
from typing import Optional, List
import platform
import subprocess
import cv2
import cv2.aruco as aruco
import paho.mqtt.client as mqtt
import requests
import serial
from serial.tools import list_ports
from wes.log_util import default_logger as logger

from barcode_scanner_controller import BarcodeScannerController
from daying_api_main import *

app = Flask(__name__)

GATEWAY_IP = "192.168.39.254"

API_URL = "http://192.168.39.24/api/aruco_data"
detector = None

# MQTT 配置
MQTT_BROKER = "192.168.39.71"
MQTT_PORT = 1883
PROPERTIES_TOPIC = "/devices/67f3887d8b3798bd77ee0039_xiangji1/sys/properties/report"
HEARTBEAT_TOPIC = "/devices/67f3887d8b3798bd77ee0039_xiangji1/sys/messages/up"
COMMAND_TOPIC = "/sys/commands/#"
PROPERTIES_SET_TOPIC = "/sys/properties/set/#"

Ser = None
SERIAL_RECONNECT_INTERVAL = 2  # 串口重连间隔时间（秒）
is_trigger_pack = False

ESP_get_device_info = "get_device_ID"  # 检查可用端口
ESP_trigger_pack = "trigger_pack"  # 触发打包

MQTT_TOPIC = "/packager/status"

LAST_TRIGGER_TIME = 0
TRIGGER_INTERVAL = 2

lock = threading.Lock()  # 线程锁

# 在MQTT配置部分添加响应话题
RESPONSE_TOPIC = "/sys/commands/response/request_id="
PROPERTIES_SET_RESPONSE_TOPIC = "/sys/properties/set/response/request_id="
# 创建 MQTT 客户端
mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
# 创建扫码类
scanner = BarcodeScannerController()


def on_connect(client, userdata, flags, rc, properties=None):
    if rc == 0:
        print("✅ MQTT111111连接成功")
    else:
        print(f"❌ MQTT1111111连接失败，错误码: {rc}")
    # 订阅控制话题
    mqtt_client.subscribe(f"/devices/67f3887d8b3798bd77ee0039_xiangji1{COMMAND_TOPIC}")
    _topic1 = f"/devices/67f4d7b7eb1760189612677f_SKU_SHIBIE{COMMAND_TOPIC}"
    print(f"topic1: {_topic1}")
    mqtt_client.subscribe(f"/devices/67f4d7b7eb1760189612677f_SKU_SHIBIE{COMMAND_TOPIC}")
    mqtt_client.subscribe(f"/devices/67ca9a973a13438b33550cea_TEST_MAC_ZXMZ{COMMAND_TOPIC}")
    mqtt_client.subscribe(f"/devices/67ca9a973a13438b33550cea_TEST_MAC_ZXMZ{PROPERTIES_SET_TOPIC}")


mqtt_client.on_connect = on_connect

LINEAR_MODULE_API = "http://192.168.39.13:8081"


# 指令下发
def on_mqtt_message(client, userdata, msg):
    """处理MQTT控制消息"""
    print(f"{msg.topic} : {msg.payload}")
    try:
        # 命令topic
        commandTopic = COMMAND_TOPIC[:-1]
        property_topic = PROPERTIES_SET_TOPIC[:-1]
        if commandTopic in msg.topic and "response" not in msg.topic:
            request_id = msg.topic.split("/")[-1].split("=")[1]
            device_id = msg.topic.split("/")[2]
            print(f"device_id::{device_id}")
            # params = msg.payload.decode()
            params = json.loads(msg.payload.decode())
            print(f"收到请求ID: {request_id}，请求参数: {params}")
            if not params["requestUrl"]:
                response = {
                    "ok": False,
                    "msg": "未设置请求URL，请求失败"
                }
            else:
                try:
                    # 修正请求方法检查
                    if params.get("requestMethod") == "POST":
                        res = requests.post(params["requestUrl"], json=params)
                    else:
                        res = requests.get(params["requestUrl"], params=params)

                    res_data = res.json()
                    response = {
                        "ok": res_data.get("code", 500) == 200,
                        "msg": res_data.get("msg", "")
                    }
                except Exception as e:
                    response = {"ok": False, "msg": f"请求处理失败: {str(e)}"}
                # mqtt返回消息
                commandResTopic = f"/devices/{device_id}{RESPONSE_TOPIC}{request_id}"
                print(f"commandResTopic::{commandResTopic}")
                mqtt_client.publish(commandResTopic, json.dumps(response))
        elif property_topic in msg.topic and "response" not in msg.topic:
            request_id = msg.topic.split("/")[-1].split("=")[1]
            device_id = msg.topic.split("/")[2]
            print(f"device_id::{device_id}")
            # params = msg.payload.decode()
            params = json.loads(msg.payload.decode())
            print(f"收到请求ID: {request_id}，请求参数: {params}")
            if not params["requestUrl"]:
                response = {
                    "ok": False,
                    "msg": "未设置请求URL，请求失败"
                }
            else:
                try:
                    # 修正请求方法检查
                    if params.get("requestMethod") == "POST":
                        res = requests.post(params["requestUrl"], json=params)
                    else:
                        res = requests.get(params["requestUrl"], params=params)

                    res_data = res.json()
                    response = {
                        "ok": res_data.get("code", 500) == 200,
                        "msg": res_data.get("msg", "")
                    }
                except Exception as e:
                    response = {"ok": False, "msg": f"请求处理失败: {str(e)}"}
                # mqtt返回消息
                command_res_topic = f"/devices/{device_id}{PROPERTIES_SET_RESPONSE_TOPIC}{request_id}"
                print(f"commandResTopic::{command_res_topic}")
                mqtt_client.publish(command_res_topic, json.dumps(response))

    except Exception as e:
        print(f"处理MQTT消息时出错: {str(e)}")


# 设置MQTT消息回调
mqtt_client.on_message = on_mqtt_message


# 货架检测类
class ArucoDetector:

    def __init__(self, aruco_id_start, aruco_id_end):
        self.Point_status = [[i, i + aruco_id_start, 0] for i in range(aruco_id_end - aruco_id_start)]
        self._running = False
        self.thread = None
        self.heartbeat_thread = None
        self.camera_connected = False
        self.cap = None
        self.camera_reconnect_attempts = 0
        self.MAX_RECONNECT_ATTEMPTS = 5
        self.RECONNECT_DELAY = 5  # 秒

    def check_camera_connection(self):
        """检查相机连接状态，如果断开则尝试重连"""
        if self.cap is None or not self.cap.isOpened():
            self.camera_connected = False
            print("⚠️ 相机连接已断开，尝试重新连接...")
            return self.reconnect_camera()
        return True

    def initialize_camera(self):
        """初始化或重新初始化相机连接"""
        if self.cap is not None:
            self.cap.release()

        self.cap = cv2.VideoCapture(0)
        if self.cap.isOpened():
            self.camera_connected = True
            self.camera_reconnect_attempts = 0
            print("✅ 相机连接成功")
            return True
        else:
            self.camera_connected = False
            print("❌ 无法连接相机")
            return False

    def reconnect_camera(self):
        """尝试重新连接相机"""

        self.camera_connected = False
        if self.cap is not None:
            self.cap.release()
        print("🔄 尝试重新连接相机...")
        while self._running:  # 在运行状态下持续尝试
            try:
                # 尝试初始化相机
                self.cap = cv2.VideoCapture(1)
                if self.cap.isOpened():
                    self.camera_connected = True
                    print("✅ 相机重新连接成功")
                    return True

                print(f"❌ 相机连接失败，{self.RECONNECT_DELAY}秒后重试...")
                time.sleep(self.RECONNECT_DELAY)

            except Exception as e:
                print(f"❌ 相机连接异常: {str(e)}")
                time.sleep(self.RECONNECT_DELAY)

        return False

    def detect_aruco_ids(self):

        if not self.camera_connected:
            if not self.reconnect_camera():
                return {"error": "相机断开连接且重连失败", "code": 503}, 503

        try:
            ret, frame = self.cap.read()
            if not ret:
                print("❌ 无法读取相机帧，尝试重新连接...")
                if not self.reconnect_camera():
                    return {"error": "无法读取帧且重连失败", "code": 500}, 500
                return {"error": "相机已重新连接，请重试"}, 503

            aruco_dict = aruco.getPredefinedDictionary(aruco.DICT_4X4_1000)
            parameters = aruco.DetectorParameters()
            corners, ids, rejected = aruco.detectMarkers(frame, aruco_dict, parameters=parameters)

            if ids is not None:
                aruco_ids = [int(id[0]) for id in ids]
                # 更新Point_status
                for point in self.Point_status:
                    point[2] = 0 if point[1] in aruco_ids else 1  # 1是无料，0是有料
                return {"Point_status": self.Point_status}, 200
            else:
                # 如果没有检测到任何ArUco标记，将所有点状态设为0
                for point in self.Point_status:
                    point[2] = 1
                return {"Point_status": self.Point_status, "code": 200}, 200

        except Exception as e:
            print(f"❌ 检测过程中发生错误: {str(e)}")
            if "capture" in str(e).lower() or "camera" in str(e).lower():
                if not self.reconnect_camera():
                    return {"error": f"相机错误: {str(e)}", "code": 500}, 500
            return {"error": str(e)}, 500

    def format_point_data(self, point_status):
        """将点状态数据格式化为指定的JSON格式"""
        formatted_data = []
        for point in point_status:
            formatted_point = {
                "point_id": point[0],
                "arucoid": point[1],
                "point_status": "1" if point[2] == 1 else "0"  # 1是满穴位，0是空穴位
            }
            formatted_data.append(formatted_point)
        return {"cave_status": formatted_data}

    def send_heartbeat(self):
        """发送心跳消息"""
        while self._running:
            # 只有当相机连接时才发送心跳
            if self.camera_connected:
                try:
                    heartbeat_msg = {
                        "message": "heartbeat",
                        "timestamp": int(time.time()),
                        "status": "online",
                        "camera_status": "connected"
                    }
                    mqtt_client.publish(HEARTBEAT_TOPIC, json.dumps(heartbeat_msg))
                    print(f"💓 心跳发送成功: {heartbeat_msg}")
                except Exception as e:
                    print(f"❌ 心跳发送失败: {e}")
            else:
                print("⚠️ 相机未连接，跳过心跳发送")
            time.sleep(1)  # 每分钟检查一次

    def aruco_detection_loop(self):
        print("🚀 ArUco 检测线程启动...")
        self._running = True

        # 启动前初始化相机
        if not self.initialize_camera():
            print("❌ 无法初始化相机，进入重连模式")
            self.reconnect_camera()

        while self._running:
            # 每次循环前检查相机连接
            if not self.check_camera_connection():
                self.reconnect_camera()
                time.sleep(2)  # 避免高频重试
                continue
            result, status_code = self.detect_aruco_ids()
            if status_code == 200:  # 确保有数据才发送
                formatted_data = self.format_point_data(result["Point_status"])
                try:
                    # 只有相机连接时才发送
                    if self.camera_connected:
                        mqtt_client.publish(PROPERTIES_TOPIC, json.dumps(formatted_data))
                        print(f"✅ 穴位数据发布成功: {formatted_data}")
                    else:
                        print("⚠️ 相机未连接，跳过数据发布")
                except Exception as e:
                    print(f"❌ 穴位数据发布失败: {e}")
            time.sleep(2)  # 每2秒检测一次

    def start_detection(self):
        if not self.thread or not self.thread.is_alive():
            self.thread = threading.Thread(target=self.aruco_detection_loop, daemon=True)
            self.thread.start()

        # 启动心跳线程
        if not self.heartbeat_thread or not self.heartbeat_thread.is_alive():
            self.heartbeat_thread = threading.Thread(target=self.send_heartbeat, daemon=True)
            self.heartbeat_thread.start()

    def stop_detection(self):
        self._running = False
        if self.cap is not None:
            self.cap.release()
            self.camera_connected = False
        if self.thread:
            self.thread.join()
        if self.heartbeat_thread:
            self.heartbeat_thread.join()


@app.route('/print_shipping_label', methods=['POST'])
def handle_print_request():
    """处理打印请求的API接口"""
    try:
        # 获取JSON数据
        data = request.get_json()

        if not data:
            return jsonify({"status": "error", "message": "No data provided"}), 400
            # 验证必需字段
        validation_result = validate_shipping_data(data)
        if not validation_result["success"]:
            return jsonify({
                "code": 400,
                "status": "error",
                "message": validation_result["message"]
            }), 400

        # 生成标签
        label_path = generate_shipping_label(data)

        # 打印标签
        success, message = print_label(label_path)

        if success:
            return jsonify({
                "code": 200,
                "status": "success",
                "message": message,
                "label_path": label_path
            }), 200
        else:
            return jsonify({
                "code": 500,
                "status": "error",
                "message": message,
                "label_path": label_path
            }), 500

    except Exception as e:
        return jsonify({
            "code": 500,
            "status": "error",
            "message": f"处理请求时发生错误: {str(e)}"
        }), 500


@app.route('/init_detector', methods=['POST'])
def init_detector():
    global detector

    data = request.get_json()
    if not data or 'aruco_id_start' not in data or 'aruco_id_end' not in data:
        return jsonify({"code": 400, "error": "缺少aruco_id_start或aruco_id_end参数"}), 400

    aruco_id_start = data['aruco_id_start']
    aruco_id_end = data['aruco_id_end']

    # 如果已有检测器，先停止
    if detector:
        detector.stop_detection()

    # 创建新检测器
    detector = ArucoDetector(aruco_id_start, aruco_id_end)
    detector.start_detection()  # 启动检测线程

    return jsonify({"code": 200, "message": "ArucoDetector初始化成功"}), 200


@app.route('/acupoints/status', methods=['GET'])
def handle_acupoints_status():
    global detector

    if detector is None:
        return jsonify({"code": 400, "error": "请先调用/init_detector初始化ArucoDetector"}), 400

    result, status_code = detector.detect_aruco_ids()
    if status_code == 200:
        formatted_data = detector.format_point_data(result["Point_status"])
        return jsonify(formatted_data), status_code
    # return jsonify(result), status_code
    return jsonify({"code": status_code, "data": result, "msg": ""}), 200


@app.route('/upBarcode', methods=['GET'])
def handle_up_barcode():
    return scanner.up_barcode()


@app.route('/openScan', methods=['GET'])
def handle_open_scan():
    isOpen = request.args.get('isOpen')
    if isOpen == "1":
        scanner_thread = threading.Thread(target=scanner.run, daemon=True)
        scanner_thread.start()
    else:
        scanner.stop()

    return scanner.open_scan(isOpen)



def initialize_serial(port, baudrate=115200, timeout=SERIAL_RECONNECT_INTERVAL):
    global Ser
    try:
        Ser = serial.Serial(port, baudrate=baudrate, timeout=timeout)
        logger.info(f"串口 {port} 已成功打开，波特率：{baudrate}")
        time.sleep(1)  # 等待串口初始化完成
        return True
    except serial.SerialException as e:
        logger.error(f"无法打开串口 {port}：{e}")
        return False

def get_available_ports():
    import sys
    if sys.platform.startswith('win'):
        ports = [port.device for port in list_ports.comports()]
    elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
        ports = ['/dev/ttyUSB%s' % i for i in range(64)] + ['/dev/ttyACM%s' % i for i in range(64)]
    elif sys.platform.startswith('darwin'):
        ports = ['/dev/cu.usbmodem%s' % i for i in range(64)] + ['/dev/cu.usbserial%s' % i for i in range(64)]
    else:
        raise EnvironmentError('Unsupported platform')
    return ports

def auto_detect_port(expected_id: str = "ESP32_dabao") -> Optional[str]:
    for port in get_available_ports():
        logger.info(f"正在检测串口: {port}")
        try:
            with serial.Serial(port, baudrate=115200, timeout=SERIAL_RECONNECT_INTERVAL) as detector:
                start_time = time.time()
                while time.time() - start_time < SERIAL_RECONNECT_INTERVAL:
                    detector.write(f"get_device_ID\n".encode('utf-8'))
                    while detector.in_waiting:
                        response = detector.readline().decode('utf-8', errors='ignore').strip()
                        if response:
                            try:
                                response_data = json.loads(response)
                                if (response_data.get("command") == "get_device_ID" and
                                    response_data.get("deviceID") == expected_id and
                                    response_data.get("status") == "ok"):
                                    logger.info(f"√ 找到目标设备在 {port}")
                                    return port
                            except json.JSONDecodeError:
                                continue
                    time.sleep(0.1)
        except serial.SerialException as e:
            logger.error(f"  {port} 检测失败: {str(e)}")
            continue
    return None

def read_serial():
    global Ser, is_trigger_pack
    while True:
        try:
            if not is_trigger_pack:
                with lock:
                    Ser.flushInput()
                    if Ser is None or not Ser.is_open:
                        logger.error("串口已断开，尝试重新连接...")
                        port = auto_detect_port()
                        if port and initialize_serial(port):
                            logger.info("串口重新连接成功")
                        else:
                            logger.error("串口重新连接失败，等待重连...")
                            time.sleep(SERIAL_RECONNECT_INTERVAL)
                            continue
                    line = Ser.readline().decode('utf-8', errors='ignore').strip()
                if line.strip():
                    if 'get_status","status' in line:
                        logger.info(f"收到串口信息：{line}")
                        try:
                            data = json.loads(line)
                            if "status" in data:
                                status_data = data["status"]
                                mqtt_client.publish(MQTT_TOPIC, json.dumps(status_data))
                        except json.JSONDecodeError as e:
                            logger.error(f"解析 JSON 错误：{e}")
        except Exception as e:
            logger.error(f"读取串口错误：{e}")
            Ser = None  # 标记串口为断开状态
            time.sleep(SERIAL_RECONNECT_INTERVAL)

@app.route('/trigger_pack', methods=['GET'])
def trigger_pack():
    global LAST_TRIGGER_TIME
    global Ser, is_trigger_pack

    is_trigger_pack = True

    if Ser is None or not Ser.is_open:
        return jsonify({
            "code": 500,
            "msg": "串口未初始化或已断开"
        }), 500

    current_time = time.time()
    if current_time - LAST_TRIGGER_TIME < TRIGGER_INTERVAL:
        return jsonify({
            "code": 429,
            "data": False,
            "msg": "操作过于频繁，请间隔2秒后重试"
        }), 429

    command = "trigger_pack\n".encode('utf-8')
    with lock:
        Ser.write(command)
        time.sleep(0.1)
        start_time = time.time()
        response = ''
        while time.time() - start_time < 5:
            line = Ser.readline().decode('utf-8', errors='ignore').strip()
            if line:
                response = line
                break
            time.sleep(0.001)
    LAST_TRIGGER_TIME = current_time
    is_trigger_pack = False

    if response:
        return jsonify({
            "code": 200,
            "data": True,
            "msg": "封口命令执行成功，收到设备响应"
        }), 200
    else:
        return jsonify({
            "code": 200,
            "data": True,
            "msg": "封口命令执行成功，但未收到设备响应"
        }), 200


def validate_shipping_data(data):
    """验证运单数据完整性"""
    # 1. 检查顶层必填字段
    required_fields = {
        'order_id': (str,),  # 订单ID
        'order_count': (int,),  # 订单数量
        'platform': (str,),  # 平台名称
        'items': (list,),  # 商品列表
        'payment': (dict,),  # 支付信息
        'customer': (dict,),  # 客户信息
        'task_info': (dict,)  # 任务信息
    }

    for field, allowed_types in required_fields.items():
        if field not in data:
            return {
                "code": 400,
                "success": False,
                "message": f"缺少必填字段: {field}"
            }
        if not isinstance(data[field], allowed_types):
            return {
                "code": 400,
                "success": False,
                "message": f"字段'{field}'类型错误，应为: {'/'.join([t.__name__ for t in allowed_types])}"
            }

    # 2. 验证支付信息
    payment_required = {
        'total': (float, int),
        'actual_pay': (float, int)
    }

    for field, allowed_types in payment_required.items():
        if field not in data['payment']:
            return {
                "code": 400,
                "success": False,
                "message": f"支付信息中缺少字段: {field}"
            }
        if not isinstance(data['payment'][field], allowed_types):
            return {
                "code": 400,
                "success": False,
                "message": f"支付字段'{field}'类型错误，应为: {'/'.join([t.__name__ for t in allowed_types])}"
            }

    # 3. 计算并验证商品总额
    calculated_total = 0.0
    for item in data['items']:
        if 'price' not in item or 'quantity' not in item:
            return {
                "code": 400,
                "success": False,
                "message": f"商品{item.get('name', '')}缺少price或quantity字段"
            }
        if not isinstance(item['price'], (float, int)) or not isinstance(item['quantity'], int):
            return {
                "code": 400,
                "success": False,
                "message": f"商品{item.get('name', '')}的price或quantity类型错误"
            }
        if item['quantity'] <= 0:
            return {
                "code": 400,
                "success": False,
                "message": f"商品{item.get('name', '')}的数量必须大于0"
            }
        calculated_total += item['price'] * item['quantity']

    calculated_total = round(calculated_total, 2)
    provided_total = round(data['payment']['total'], 2)

    if abs(calculated_total - provided_total) > 0.01:
        return {
            "code": 400,
            "success": False,
            "message": f"支付总额不匹配，计算值:{calculated_total}，提供值:{provided_total}"
        }

    # 4. 验证客户信息
    customer_required = {
        'name': (str,),
        'phone': (str,),
        'address': (str,)
    }

    for field, allowed_types in customer_required.items():
        if field not in data['customer']:
            return {
                "code": 400,
                "success": False,
                "message": f"客户信息中缺少字段: {field}"
            }
        if not isinstance(data['customer'][field], allowed_types):
            return {
                "code": 400,
                "success": False,
                "message": f"客户字段'{field}'类型错误，应为: {'/'.join([t.__name__ for t in allowed_types])}"
            }

    # 5. 验证任务信息
    task_required = {
        'task_id': (int, str),
        'robot_name': (str,)
    }

    for field, allowed_types in task_required.items():
        if field not in data['task_info']:
            return {
                "code": 400,
                "success": False,
                "message": f"任务信息中缺少字段: {field}"
            }
        if not isinstance(data['task_info'][field], allowed_types):
            return {
                "code": 400,
                "success": False,
                "message": f"任务字段'{field}'类型错误，应为: {'/'.join([t.__name__ for t in allowed_types])}"
            }

    # 6. 验证商品列表
    if len(data['items']) == 0:
        return {
            "code": 400,
            "success": False,
            "message": "商品列表不能为空"
        }

    item_required = {
        'name': (str,),
        'price': (float, int),
        'quantity': (int,)
    }

    for idx, item in enumerate(data['items'], 1):
        for field, allowed_types in item_required.items():
            if field not in item:
                return {
                    "code": 400,
                    "success": False,
                    "message": f"商品#{idx} 缺少字段: {field}"
                }
            if not isinstance(item[field], allowed_types):
                return {
                    "code": 400,
                    "success": False,
                    "message": f"商品#{idx} 字段'{field}'类型错误，应为: {'/'.join([t.__name__ for t in allowed_types])}"
                }
        if item['quantity'] <= 0:
            return {
                "code": 400,
                "success": False,
                "message": f"商品#{idx} 数量必须大于0"
            }

    return {
        "code": 200,
        "success": True,
        "message": "数据验证通过"
    }


def init_detector():
    global detector
    # 如果已有检测器，先停止
    if detector:
        detector.stop_detection()
    # 创建新检测器
    try:
        detector = ArucoDetector(0, 9)
        detector.start_detection()  # 启动检测线程
        return True
    except:
        print("检测器 初始化异常")
        return False

def init_mqtt():
    try:
        mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60)
        mqtt_client.loop_start()
        return True
    except:
        print("mqtt 初始化失败")
        return False



def check_ip(ip):
    global output
    try:
        if platform.system().lower() == "windows":
            output = subprocess.run(
                ["ping", "-n", "1", "-w", "500", ip],  # 超时时间500毫秒
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL
            )
        if output.returncode == 0:
            print(f"当前网络已联通 ✔")
            return True
        else:
            print(f"当前网络未联通 ×")
            return False
    except Exception as e:
        return False

if __name__ == '__main__':
    port = None
    # 检查网络
    while True:
        time.sleep(3)
        print(f"检查网关是否联通：{GATEWAY_IP}")
        ping_gateway = check_ip(GATEWAY_IP)
        port = auto_detect_port("ESP32_dabao")
        if ping_gateway and port:
            if initialize_serial(port) and init_mqtt() and init_detector():
                break

    # 发布打包机信号
    serial_thread = threading.Thread(target=read_serial)
    serial_thread.daemon = True
    serial_thread.start()

    app.run(host='0.0.0.0', port=8081, debug=False)